/*
 * basic.c
 *
 *  Created on: Apr 17, 2016
 *      Author: root
 */

#include <string.h>
#include "basic.h"
#include "operator.h"

int hash_fn(const char *key, int table_size) {
	const char *c;
	unsigned int hashed = 0;
	for (c = key; *c; c++) {
		int bits_per_int = sizeof(unsigned int) * 8;
		/* letters have about 5 bits in them */
		hashed = (hashed << 5) | (hashed >> (bits_per_int - 5));
		hashed ^= *c;
	}
	return hashed % table_size;
}

void add_slot_by_symbol(Scheme *sc, Cell* vec, Cell* symbol, Cell* value) {
    char* symbolName = symbol_name(symbol);
    value->_name = symbolName;
	Cell* slot = immutable_cons(sc, symbol, value);
	int location = hash_fn(symbolName, vec->_longValue);
	Cell* item = get_vector_item(vec, location);
	set_vector_item(vec, location, immutable_cons(sc, slot, item));
}

//replace when existed
void replace_slot_by_symbol(Scheme *sc, Cell* vec, Cell* symbol, Cell* value) {
	char* symbolName = symbol_name(symbol);
    value->_name = symbolName;
	Cell* slot = immutable_cons(sc, symbol, value);
	int location = hash_fn(symbolName, vec->_longValue);
	Cell* item = get_vector_item(vec, location);
	Cell* p = NULL;
	for (p = item; p != G_NIL; p = cdr(p)) {
		char* symname = symbol_name(car(p));
		if (strcmp(symbolName, symname) == 0) {
			CAR(p) = slot;
			return;
		}
	}
	set_vector_item(vec, location, immutable_cons(sc, slot, item));
}

void delete_slot_by_symbol(Scheme *sc, Cell* vec, Cell* symbol) {
	char* symbolName = symbol_name(symbol);
	int location = hash_fn(symbolName, vec->_longValue);
	Cell* item = get_vector_item(vec, location);
	Cell* p = NULL;
	for (p = item; p != G_NIL; p = cdr(p)) {
		char* symname = symbol_name(car(p));
		if (strcmp(symbolName, symname) == 0) {
			CAR(p) = cdr(p);
			return;
		}
	}
}

Cell* find_slot_by_symbol(Scheme *sc, Cell* vec, Cell* symbol) {
	char* symbolName = symbol_name(symbol);
	int location = hash_fn(symbolName, vec->_longValue);
	Cell* item = get_vector_item(vec, location);
	Cell* p = NULL;
	for (p = item; p != G_NIL; p = car(p)) {
		char* symname = symbol_name(car(p));
		if (strcmp(symbolName, symname) == 0)
			return cdr(p);
	}
	return G_NIL;
}

int cell_contains_element(Scheme *sc, Cell *cell, Cell *element) {
	if (!type_of(sc, cell, PAIR_TYPE) || type_of(sc, element, PAIR_TYPE)) {
		return FALSE;
	}
	Cell *e = NULL;
	for (e = cell; e != G_NIL; e = cdr(e)) {
		if (eqv(sc, element, e))
			return TRUE;
	}
	return FALSE;
}

/*
static char* get_name_of_procedure_recur(Scheme *sc, Cell* recur, Cell* cell) {

	// Cell* temp = find_slot_by_symbol(sc, recur, make_symbol(sc, "$toplevel"));

	if (recur == NULL) {
		return NULL;
	}
	if (recur == G_NIL) {
		return "";
	}

	if (type_of(recur, VECTOR_TYPE)) {
		int num = recur->_longValue / 2 + recur->_longValue % 2;
		Cell* p = NULL;
		int i;
		for (i = 0; i < num; i++) {
			Cell* item = get_vector_item(recur, i);
			char *symname = NULL;
			for (p = item; p != G_NIL; p = car(p)) {
				if (type_of(car(p), SYMBOL_TYPE)) {
					if (cdr(p) == cell) {
						symname = symbol_name(car(p));
						return symname;
					} else {
						break;
					}
				}
			}
		}
	}

	return "";
}

static char* get_name_of_procedure(Scheme *sc, Cell* cell) {
	return get_name_of_procedure_recur(sc, sc->globalObjects, cell);
}
*/

//TODO
char* cell2str(Scheme *sc, Cell* cell) {
	Cell *x;
	char* ret = init_string();

	char* the_type = init_string();
    if (cell->_type != NULL) {
        the_type = dyna_strcat(the_type, "#<");
        the_type = dyna_strcat(the_type, cell->_type);
        the_type = dyna_strcat(the_type, ">: ");
    }
	char* the_value;

	if (cell == G_NIL || (cell->_name != NULL && strcmp(cell->_name, CONSTANT_NAME_NIL) == 0)) {
		ret = "()";
	} else if (type_of(sc, cell, VECTOR_TYPE)) {
		ret = dyna_strcat(the_type, "#(");
		int index = 0;
		int len = cell->_longValue;
		for (index = 0; index < len; index++) {
			Cell* elem = get_vector_item(cell, index);
			if (index > 0)
				ret = dyna_strcat(ret, " ");
			ret = dyna_strcat(ret, cell2str(sc, elem));
		}
		ret = dyna_strcat(ret, ")");
	} else if (type_of(sc, cell, ENVIRONMENT_TYPE)) { //环境
		ret = dyna_strcat(ret, "#<Environment>");
		ret = dyna_strcat(ret, cell2str(sc, car(cell))); // Vector
	} else if (!type_of(sc, cell, PAIR_TYPE)) { //原子
		ret = dyna_strcat(ret, the_type);
		if (cell == G_TRUE) {
			the_value = "#t";
		} else if (cell == G_FALSE) {
			the_value = "#f";
		} else if (type_of(sc, cell, SYNTAX_TYPE)) {
			the_value = cell->_name;
		} else if (type_of(sc, cell, SYMBOL_TYPE)) {
			the_value = symbol_name(cell);
		} else if (type_of(sc, cell, NUMBER_TYPE)) {
			the_value = number_to_string(sc, cell);
		} else if (type_of(sc, cell, STRING_TYPE)) {
			the_value = cell->_string;
		} else if (type_of(sc, cell, CHARACTER_TYPE)) {
			the_value = char2string(cell->_longValue);
		} else {
			the_value = cell->_name;
		}
		ret = dyna_strcat(ret, the_value);
	} else if (car(cell) == cell_quote && (type_of(sc, cdr(cell), PAIR_TYPE) && cddr(cell) == G_NIL)) {
		ret = dyna_strcat(ret, "'");
		ret = dyna_strcat(ret, cell2str(sc, cadr(cell)));
	} else {
		ret = dyna_strcat(ret, "(");
		if (type_of(sc, cell, PAIR_TYPE)) {
			for (x = cell; type_of(sc, x, PAIR_TYPE) && cdr(x) != G_NIL; x = cdr(x)) {
				if (type_of(sc, x, PAIR_TYPE)) {
					ret = dyna_strcat(ret, cell2str(sc, car(x)));
				} else {
					ret = dyna_strcat(ret, cell2str(sc, x));
				}
				if (type_of(sc, x, PAIR_TYPE)) {
					if (!type_of(sc, cdr(x), PAIR_TYPE)) {
						ret = dyna_strcat(ret, " . ");
					} else {
						ret = dyna_strcat(ret, " ");
					}
				}
			}
			if (type_of(sc, x, PAIR_TYPE)) {
				ret = dyna_strcat(ret, cell2str(sc, car(x)));
			} else {
				ret = dyna_strcat(ret, cell2str(sc, x));
			}
		}
		ret = dyna_strcat(ret, ")");
	}

	return ret;
}

void push_callstack(Scheme *sc, Cell* op, Cell* args, Cell* code) {
	sc->callStack = cons(sc, code, sc->callStack);
	sc->callStack = cons(sc, sc->env, sc->callStack);
	sc->callStack = cons(sc, args, sc->callStack);
	sc->callStack = cons(sc, op, sc->callStack);
}

Cell* pop_callstack(Scheme *sc) {
	if (sc->callStack == G_NIL)
		return G_NIL;
	sc->op = first(sc->callStack);
	sc->args = second(sc->callStack);
	sc->env = third(sc->callStack);
	sc->code = fourth(sc->callStack);
	sc->callStack = cddddr(sc->callStack);
	return G_TRUE;
}

Cell* pick_current_item(Scheme *sc, int type) {
	switch (type) {
		case ITEM_OPERATOR:
			return sc->op;
		case ITEM_ARGUMENTS:
			return sc->args;
		case ITEM_ENVIRONMENT:
			return sc->env;
		case ITEM_CODE:
			return sc->code;
		default:
			return G_NIL;
	}
}

Cell* pick_callstack_item(Scheme *sc, int type) {
	if (sc->callStack == G_NIL)
		return G_NIL;
	switch (type) {
		case ITEM_OPERATOR:
			return first(sc->callStack);
		case ITEM_ARGUMENTS:
			return second(sc->callStack);
		case ITEM_ENVIRONMENT:
			return third(sc->callStack);
		case ITEM_CODE:
			return fourth(sc->callStack);
		default:
			return G_NIL;
	}
}
